package tests.bckup;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_COLOR_MATERIAL;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_DIFFUSE;
import static org.lwjgl.opengl.GL11.GL_FLAT;
import static org.lwjgl.opengl.GL11.GL_LIGHT0;
import static org.lwjgl.opengl.GL11.GL_LIGHTING;
import static org.lwjgl.opengl.GL11.GL_LIGHT_MODEL_AMBIENT;
import static org.lwjgl.opengl.GL11.GL_LINEAR;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_NORMALIZE;
import static org.lwjgl.opengl.GL11.GL_POSITION;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.GL_RGB;
import static org.lwjgl.opengl.GL11.GL_SMOOTH;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glGenTextures;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glNormal3f;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
import static org.lwjgl.opengl.GL11.glRotatef;
import static org.lwjgl.opengl.GL11.glShadeModel;
import static org.lwjgl.opengl.GL11.glTexCoord2f;
import static org.lwjgl.opengl.GL11.glTexImage2D;
import static org.lwjgl.opengl.GL11.glTexParameteri;
import static org.lwjgl.opengl.GL11.glTranslatef;
import static org.lwjgl.opengl.GL11.glVertex3f;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.util.glu.GLU.gluPerspective;

import java.awt.Image;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.Vector;

import org.lwjgl.util.glu.Sphere;
import org.lwjgl.util.vector.Vector3f;


class CollisionDetector {
	
	private final int RAND_MAX = 0x7fff;
	
	float randomFloat() {
		return (float)Math.random() / ((float)RAND_MAX + 1);
	}

	private final  float GRAVITY = 8.0f;
	private final  float BOX_SIZE = 12.0f; //The length of one side of the box
	//The amount of time between each time that we handle collisions and apply the
	//effects of gravity
	private final float TIME_BETWEEN_UPDATES = 0.01f;
	private final int TIMER_MS = 25; //The number of milliseconds to which the timer is set

	//Stores information regarding a ball
	class Ball {
		Vector3f v; //Velocity
		Vector3f pos; //Position
		float r; //Radius
		Vector3f color;
	};

	private enum Wall {WALL_LEFT, WALL_RIGHT, WALL_FAR, WALL_NEAR, WALL_TOP, WALL_BOTTOM};

	//Stores a pair of balls
	class BallPair {
		Ball ball1;
		Ball ball2;
	};

	//Stores a ball and a wall
	class BallWallPair {
		Ball ball;
		Wall wall;
	};

	private final int MAX_OCTREE_DEPTH = 6;
	private final int MIN_BALLS_PER_OCTREE = 3;
	private final int MAX_BALLS_PER_OCTREE = 6;

	//Our data structure for making collision detection faster
	class Octree {

			Vector3f corner1; //(minX, minY, minZ)
			Vector3f corner2; //(maxX, maxY, maxZ)
			Vector3f center;//((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2)
			
			/* The children of this, if this has any.  children[0][*][*] are the
			 * children with x coordinates ranging from minX to centerX.
			 * children[1][*][*] are the children with x coordinates ranging from
			 * centerX to maxX.  Similarly for the other two dimensions of the
			 * children array.
			 */
			Octree[][][] children = new Octree[2][2][2];
			//Whether this has children
			boolean hasChildren;
			//The balls in this, if this doesn't have any children
			Ball[] balls;
			//The depth of this in the tree
			int depth;
			//The number of balls in this, including those stored in its children
			int numBalls;
			
			//Adds a ball to or removes one from the children of this
			void fileBall(Ball ball, Vector3f pos, boolean addBall) {
				//Figure out in which child(ren) the ball belongs
				for(int x = 0; x < 2; x++) {
					if (x == 0) {
						if (pos.x - ball.r > center.x) {
							continue;
						}
					}
					else if (pos.x + ball.r < center.x) {
						continue;
					}
					
					for(int y = 0; y < 2; y++) {
						if (y == 0) {
							if (pos.y - ball.r > center.y) {
								continue;
							}
						}
						else if (pos.y + ball.r < center.y) {
							continue;
						}
						
						for(int z = 0; z < 2; z++) {
							if (z == 0) {
								if (pos.z - ball.r > center.z) {
									continue;
								}
							}
							else if (pos.z + ball.r < center.z) {
								continue;
							}
							
							//Add or remove the ball
							if (addBall) {
								children[x][y][z].add(ball);
							}
							else {
								children[x][y][z].remove(ball, pos);
							}
						}
					}
				}
			}
			
			//Creates children of this, and moves the balls in this to the children
			void haveChildren() {
				for(int x = 0; x < 2; x++) {
					float minX;
					float maxX;
					if (x == 0) {
						minX = corner1.x;
						maxX = center.x;
					}
					else {
						minX = center.x;
						maxX = corner2.x;
					}
					
					for(int y = 0; y < 2; y++) {
						float minY;
						float maxY;
						if (y == 0) {
							minY = corner1.y;
							maxY = center.y;
						}
						else {
							minY = center.y;
							maxY = corner2.y;
						}
						
						for(int z = 0; z < 2; z++) {
							float minZ;
							float maxZ;
							if (z == 0) {
								minZ = corner1.z;
								maxZ = center.z;
							}
							else {
								minZ = center.z;
								maxZ = corner2.z;
							}
							
							children[x][y][z] = new Octree(new Vector3f(minX, minY, minZ),
															new Vector3f(maxX, maxY, maxZ),
														   depth + 1);
						}
					}
				}
				
				//Remove all balls from "balls" and add them to the new children
				/**
				 * TODO Check the for loop...
				 */
//				for(set<Ball>::iterator it = balls.begin(); it != balls.end();
				for(int it = 0; it != balls.length; it++) {
					Ball ball = balls[it];
					fileBall(ball, ball.pos, true);
				}
//				balls.clear();
				
				hasChildren = true;
			}
			
			//Adds all balls in this or one of its descendants to the specified set
			void collectBalls(Vector<Ball> bs) {
				if (hasChildren) {
					for(int x = 0; x < 2; x++) {
						for(int y = 0; y < 2; y++) {
							for(int z = 0; z < 2; z++) {
								children[x][y][z].collectBalls(bs);
							}
						}
					}
				}
				else {
					/**
					 * TODO Check the for loop...
					 */
//					for(set<Ball>::iterator it = balls.begin(); it != balls.end();
					for(int it = 0; it != balls.length; it++) {
						Ball ball = balls[it];
						bs.add(ball);
					}
				}
			}
			
			//Destroys the children of this, and moves all balls in its descendants
			//to the "balls" set
			void destroyChildren() {
				//Move all balls in descendants of this to the "balls" set
//				collectBalls(balls);
				
				for(int x = 0; x < 2; x++) {
					for(int y = 0; y < 2; y++) {
						for(int z = 0; z < 2; z++) {
//							delete children[x][y][z];
						}
					}
				}
				
				hasChildren = false;
			}
			
			//Removes the specified ball at the indicated position
			public void remove(Ball ball, Vector3f pos) {
				numBalls--;
				
				if (hasChildren && numBalls < MIN_BALLS_PER_OCTREE) {
					destroyChildren();
				}
				
				if (hasChildren) {
					fileBall(ball, pos, false);
				}
				else {
//					balls.erase(ball);
				}
			}
			
			/* Helper fuction for potentialBallWallCollisions(vector).  Adds
			 * potential ball-wall collisions to cs, where w is the type of wall,
			 * coord is the relevant coordinate of the wall ('x', 'y', or 'z'), and
			 * dir is 0 if the wall is in the negative direction and 1 if it is in
			 * the positive direction.  Assumes that this is in the extreme
			 * direction of the coordinate, e.g. if w is WALL_TOP, the function
			 * assumes that this is in the far upward direction.
			 */
			public void potentialBallWallCollisions(Vector<BallWallPair> cs, Wall w, char coord, int dir) {
				if (hasChildren) {
					//Recursively call potentialBallWallCollisions on the correct
					//half of the children (e.g. if w is WALL_TOP, call it on
					//children above centerY)
					for(int dir2 = 0; dir2 < 2; dir2++) {
						for(int dir3 = 0; dir3 < 2; dir3++) {
							Octree child = new Octree(new Vector3f(), new Vector3f(), 0);
							switch (coord) {
								case 'x':
									child = children[dir][dir2][dir3];
									break;
								case 'y':
									child = children[dir2][dir][dir3];
									break;
								case 'z':
									child = children[dir2][dir3][dir];
									break;
							}
							
							child.potentialBallWallCollisions(cs, w, coord, dir);
						}
					}
				}
				else {
					//Add (ball, w) for all balls in this
					/**
					 * TODO Check the for loop...
					 */
//					for(set<Ball>::iterator it = balls.begin(); it != balls.end();
					for(int it = 0; it != balls.length; it++) {
						Ball ball = balls[it];
						BallWallPair bwp = new BallWallPair();
						bwp.ball = ball;
						bwp.wall = w;
						cs.add(bwp);
					}
				}
			}
			//Constructs a new Octree.  c1 is (minX, minY, minZ), c2 is (maxX, maxY,
			//maxZ), and d is the depth, which starts at 1.
			Octree(Vector3f c1, Vector3f c2, int d) {
				corner1 = c1;
				corner2 = c2;
				Vector3f.add(c2, c2, center);
				center.x /= 2;
				center.y /= 2;
				center.z /= 2;
//				center = (c1 + c2) / 2;
				depth = d;
				numBalls = 0;
				hasChildren = false;
			}
			
			//Adds a ball to this
			void add(Ball ball) {
				numBalls++;
				if (!hasChildren && depth < MAX_OCTREE_DEPTH &&
					numBalls > MAX_BALLS_PER_OCTREE) {
					haveChildren();
				}
				
				if (hasChildren) {
					fileBall(ball, ball.pos, true);
				}
				else {
					balls[numBalls] = ball;
//					balls.push(ball);
				}
			}
			
			//Removes a ball from this
			public void remove(Ball ball) {
				remove(ball, ball.pos);
			}
			
			//Changes the position of a ball in this from oldPos to ball.pos
			private void ballMoved(Ball ball, Vector3f oldPos) {
				remove(ball, oldPos);
				add(ball);
			}
			
			//Adds potential ball-ball collisions to the specified set
			private void potentialBallBallCollisions(Vector<BallPair> collisions) {
				if (hasChildren) {
					for(int x = 0; x < 2; x++) {
						for(int y = 0; y < 2; y++) {
							for(int z = 0; z < 2; z++) {
								children[x][y][z].
									potentialBallBallCollisions(collisions);
							}
						}
					}
				}
				else {
					//Add all pairs (ball1, ball2) from balls
					//Add (ball, w) for all balls in this
					/**
					 * TODO Check the for loop...
					 */
//					for(set<Ball>::iterator it = balls.begin(); it != balls.end();
					for(int it = 0; it != balls.length; it++) {
						Ball ball1 = balls[it];
						for(int it2 = 0; it2 != balls.length; it2++) {
							Ball ball2 =balls[it2];
							//This test makes sure that we only add each pair once
							if (ball1.pos.x < ball2.pos.x && ball1.pos.y < ball2.pos.y && ball1.pos.z < ball1.pos.z) {
								BallPair bp = new BallPair();
								bp.ball1 = ball1;
								bp.ball2 = ball2;
								collisions.add(bp);
							}
						}
					}
				}
			}
			
			//Adds potential ball-wall collisions to the specified set
			private void potentialBallWallCollisions(Vector<BallWallPair> collisions) {
				potentialBallWallCollisions(collisions, Wall.WALL_LEFT, 'x', 0);
				potentialBallWallCollisions(collisions, Wall.WALL_RIGHT, 'x', 1);
				potentialBallWallCollisions(collisions, Wall.WALL_BOTTOM, 'y', 0);
				potentialBallWallCollisions(collisions, Wall.WALL_TOP, 'y', 1);
				potentialBallWallCollisions(collisions, Wall.WALL_FAR, 'z', 0);
				potentialBallWallCollisions(collisions, Wall.WALL_NEAR, 'z', 1);
			}
	};

	//Puts potential ball-ball collisions in potentialCollisions.  It must return
	//all actual collisions, but it need not return only actual collisions.
	void potentialBallBallCollisions(Vector<BallPair> potentialCollisions, Vector<Ball> balls, Octree octree) {
		//Fast method
		octree.potentialBallBallCollisions(potentialCollisions);
		
		/*
		//Slow method
		for(unsigned int i = 0; i < balls.size(); i++) {
			for(unsigned int j = i + 1; j < balls.size(); j++) {
				BallPair bp;
				bp.ball1 = balls[i];
				bp.ball2 = balls[j];
				potentialCollisions.push_back(bp);
			}
		}
		*/
	}

	//Puts potential ball-wall collisions in potentialCollisions.  It must return
	//all actual collisions, but it need not return only actual collisions.
	void potentialBallWallCollisions(Vector<BallWallPair> potentialCollisions,
									 Vector<Ball> balls, Octree octree) {
		//Fast method
		octree.potentialBallWallCollisions(potentialCollisions);
		
		/*
		//Slow method
		Wall walls[] =
			{WALL_LEFT, WALL_RIGHT, WALL_FAR, WALL_NEAR, WALL_TOP, WALL_BOTTOM};
		for(unsigned int i = 0; i < balls.size(); i++) {
			for(int j = 0; j < 6; j++) {
				BallWallPair bwp;
				bwp.ball = balls[i];
				bwp.wall = walls[j];
				potentialCollisions.push_back(bwp);
			}
		}
		*/
	}

	//Moves all of the balls by their velocity times dt
	void moveBalls(Vector<Ball> balls, Octree octree, float dt) {
		for(int i = 0; i < balls.size(); i++) {
			Ball ball = balls.get(i);
			Vector3f oldPos = ball.pos;
			ball.v.scale(dt);
			ball.pos.x += ball.v.x;
			ball.pos.y += ball.v.y;
			ball.pos.z += ball.v.z;
			
//			ball.pos += ball.v * dt;
			octree.ballMoved(ball, oldPos);
		}
	}

	//Decreases the y coordinate of the velocity of each ball by GRAVITY *
	//TIME_BETWEEN_UPDATES
	void applyGravity(Vector<Ball> balls) {
		for(int i = 0; i < balls.size(); i++) {
			Ball ball = balls.get(i);
			ball.v.y -= GRAVITY * TIME_BETWEEN_UPDATES;
//			ball.v -= new Vector3f(0, GRAVITY * TIME_BETWEEN_UPDATES, 0);
		}
	}

	//Returns whether two balls are colliding
	boolean testBallBallCollision(Ball b1, Ball b2) {
		//Check whether the balls are close enough
		float r = b1.r + b2.r;
		Vector3f minus = new Vector3f();
		Vector3f.sub(b1.pos, b2.pos, minus);
		float square = minus.lengthSquared();
		if (square < r * r) {
			//Check whether the balls are moving toward each other
			Vector3f netVelocity = new Vector3f();
			Vector3f.sub(b1.v, b2.v, netVelocity);
			Vector3f displacement = new Vector3f();
			Vector3f.sub(b1.pos, b2.pos, displacement);
			
			float ret = Vector3f.dot(netVelocity, displacement);
			
			return ret < 0;
		}
		else
			return false;
	}

	//Handles all ball-ball collisions
	void handleBallBallCollisions(Vector<Ball> balls, Octree octree) {
		Vector<BallPair> bps = new Vector<CollisionDetector.BallPair>();
		potentialBallBallCollisions(bps, balls, octree);
		for(int i = 0; i < bps.size(); i++) {
			BallPair bp = bps.get(i);
			
			Ball b1 = bp.ball1;
			Ball b2 = bp.ball2;
			if (testBallBallCollision(b1, b2)) {
				//Make the balls reflect off of each other
//				Vector3f displacement = (b1.pos - b2.pos).normalize();
				Vector3f a = new Vector3f();
				Vector3f.sub(b1.pos, b2.pos, a);
				Vector3f displacement = new Vector3f();
				a.normalise(displacement);
				
				
				float b1Dot = Vector3f.dot(b1.v, displacement);
				float b2Dot = Vector3f.dot(b2.v, displacement);
				
				Vector3f finalVec = new Vector3f();
				
				b1.v.x -= 2 * displacement.x * b1Dot;
				b1.v.y -= 2 * displacement.y * b1Dot;
				b1.v.z -= 2 * displacement.z * b1Dot;
				
				b2.v.x -= 2 * displacement.x * b2Dot;
				b2.v.y -= 2 * displacement.y * b2Dot;
				b2.v.z -= 2 * displacement.z * b2Dot;
			}
		}
	}

	//Returns the direction from the origin to the wall
	Vector3f wallDirection(Wall wall) {
		switch (wall) {
			case WALL_LEFT:
				return new Vector3f(-1, 0, 0);
			case WALL_RIGHT:
				return new Vector3f(1, 0, 0);
			case WALL_FAR:
				return new Vector3f(0, 0, -1);
			case WALL_NEAR:
				return new Vector3f(0, 0, 1);
			case WALL_TOP:
				return new Vector3f(0, 1, 0);
			case WALL_BOTTOM:
				return new Vector3f(0, -1, 0);
			default:
				return new Vector3f(0, 0, 0);
		}
	}

	//Returns whether a ball and a wall are colliding
	boolean testBallWallCollision(Ball ball, Wall wall) {
		Vector3f dir = wallDirection(wall);
		//Check whether the ball is far enough in the "dir" direction, and whether
		//it is moving toward the wall
		float posDot = Vector3f.dot(ball.pos, dir);
		float vDot = Vector3f.dot(ball.v, dir);
		
		return posDot + ball.r > BOX_SIZE / 2 && vDot > 0;
	}

	//Handles all ball-wall collisions
	void handleBallWallCollisions(Vector<Ball> balls, Octree octree) {
		Vector<BallWallPair> bwps = new Vector<CollisionDetector.BallWallPair>();
		potentialBallWallCollisions(bwps, balls, octree);
		for(int i = 0; i < bwps.size(); i++) {
			BallWallPair bwp = bwps.get(i);
			
			Ball b = bwp.ball;
			Wall w = bwp.wall;
			if (testBallWallCollision(b, w)) {
				//Make the ball reflect off of the wall
				Vector3f dir = new Vector3f();
				wallDirection(w).normalise(dir);
				
				float vDot = Vector3f.dot(b.v, dir);

				b.v.x -= 2 * dir.x * vDot;
				b.v.y -= 2 * dir.y * vDot;
				b.v.z -= 2 * dir.z * vDot;
			}
		}
	}

	//Applies gravity and handles all collisions.  Should be called every
	//TIME_BETWEEN_UPDATES seconds.
	void performUpdate(Vector<Ball> balls, Octree octree) {
		applyGravity(balls);
		handleBallBallCollisions(balls, octree);
		handleBallWallCollisions(balls, octree);
	}

	//Advances the state of the balls by t.  timeUntilUpdate is the amount of time
	//until the next call to performUpdate.
	void advance(Vector<Ball> balls, Octree octree, float t, float timeUntilUpdate) {
		while (t > 0) {
			if (timeUntilUpdate <= t) {
				moveBalls(balls, octree, timeUntilUpdate);
				performUpdate(balls, octree);
				t -= timeUntilUpdate;
				timeUntilUpdate = TIME_BETWEEN_UPDATES;
			}
			else {
				moveBalls(balls, octree, t);
				timeUntilUpdate -= t;
				t = 0;
			}
		}
	}





	private Vector<Ball> _balls; //All of the balls in play
	private float _angle = 0.0f; //The camera angle
	private Octree _octree; //An octree with all af the balls
	//The amount of time until performUpdate should be called
	private float _timeUntilUpdate = 0;
	private int _textureId;

	//Deletes everything.  This should be called when exiting the program.
//	void cleanup() {
//		for(int i = 0; i < _balls.size(); i++) {
//			delete _balls;
//		}
//		delete _octree;
//	}

	void handleKeypress(int key, int x, int y) {
		switch (key) {
			case 27: //Escape key
//				cleanup();
				System.exit(0);
			case ' ':
				//Add 20 balls with a random position, velocity, radius, and color
				for(int i = 0; i < 20; i++) {
					Ball ball = new Ball();
					ball.pos = new Vector3f(8 * randomFloat() - 4, 8 * randomFloat() - 4, 8 * randomFloat() - 4);
					ball.v = new Vector3f(8 * randomFloat() - 4, 8 * randomFloat() - 4, 8 * randomFloat() - 4);
					ball.r = 0.1f * randomFloat() + 0.1f;
					ball.color = new Vector3f(0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f);
					_balls.add(ball);
					_octree.add(ball);
				}
		}
	}

	int loadTexture(Image image) {
		IntBuffer   textureId = createIntBuffer(1);
		glGenTextures(textureId);
		glBindTexture(GL_TEXTURE_2D, textureId.get());
//		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 200, 200, 0,
//					 GL_RGB,
//					 GL_UNSIGNED_BYTE,
//					 image.gr);
		return textureId.get();
	}

	void initRendering() {
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glEnable(GL_NORMALIZE);
		glEnable(GL_COLOR_MATERIAL);
		
//		Image image = loadBMP("vtr.bmp");
//		_textureId = loadTexture(image);
//		delete image;
	}

	void handleResize(int w, int h) {
		glViewport(0, 0, w, h);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45.0f, (float)w / (float)h, 1.0f, 200.0f);
	}

	void drawScene() {
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		glMatrixMode(GL_MODELVIEW);
	    glLoadIdentity();
		glTranslatef(0.0f, 0.0f, -20.0f);
		glRotatef(-_angle, 0.0f, 1.0f, 0.0f);
		
//		float ambientColor[] = {0.5f, 0.5f, 0.5f, 1.0f};
//		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
//		
//		float lightColor[] = {0.7f, 0.7f, 0.7f, 1.0f};
//		float lightPos[] = {1.0f, 0.2f, 0.0f, 0.0f};
//		glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
//		glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
		
		//Draw the top and the bottom of the box
		glShadeModel(GL_FLAT);
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, _textureId);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glColor3f(1.0f, 1.0f, 1.0f);
		glBegin(GL_QUADS);
		
		glNormal3f(0.0f, 1.0f, 0.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-BOX_SIZE / 2, -BOX_SIZE / 2, BOX_SIZE / 2);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(BOX_SIZE / 2, -BOX_SIZE / 2, BOX_SIZE / 2);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2);
		
		glNormal3f(0.0f, -1.0f, 0.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-BOX_SIZE / 2, BOX_SIZE / 2, -BOX_SIZE / 2);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(BOX_SIZE / 2, BOX_SIZE / 2, -BOX_SIZE / 2);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2);
		
		glEnd();
		glShadeModel(GL_SMOOTH);
		glDisable(GL_TEXTURE_2D);
		
		//Draw the balls
		for(int i = 0; i < _balls.size(); i++) {
			Ball ball = _balls.get(i);
			glPushMatrix();
			glTranslatef(ball.pos.x, ball.pos.y, ball.pos.z);
			glColor3f(ball.color.x, ball.color.y, ball.color.z);
			Sphere s = new Sphere();
//			s.
//			s.draw(ball.r, 0, 0);
			
//			glutSolidSphere(ball.r, 12, 12); //Draw a sphere
			glPopMatrix();
		}
		
//		glutSwapBuffers();
	}

	//Called every TIMER_MS milliseconds
	void update(int value) {
		advance(_balls, _octree, (float)TIMER_MS / 1000.0f, _timeUntilUpdate);
		_angle += (float)TIMER_MS / 100;
		if (_angle > 360) {
			_angle -= 360;
		}
		
//		glutPostRedisplay();
//		glutTimerFunc(TIMER_MS, update, 0);
	}

	int main(int argc, char argv) {
//		srand((int time(0)); //Seed the random number generator
		
//		glutInit(&argc, argv);
//		glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//		glutInitWindowSize(400, 400);
//		
//		glutCreateWindow("Collision Detection - videotutorialsrock.com");
		initRendering();
		
		_octree = new Octree(new Vector3f(-BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2),
							 new Vector3f(BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2), 1);
		
//		glutDisplayFunc(drawScene);
//		glutKeyboardFunc(handleKeypress);
//		glutReshapeFunc(handleResize);
//		glutTimerFunc(TIMER_MS, update, 0);
//		
//		glutMainLoop();
		return 0;
	}
	private static IntBuffer createIntBuffer(int numElements){
	    return ByteBuffer.allocateDirect(4*numElements).order(
	           ByteOrder.nativeOrder()).asIntBuffer();
	  }

}
